/******************************************************************************
                         COPYRIGHT 1955-2008 by BUPT ISC.
                        北京邮电大学信息安全中心 版权所有
                           Web: http://www.bupt.edu.cn
 *******************************************************************************/
/*******************************************************************************
  文件名： log.c
  项目：   
  模块：   log
  作者：   huangwei
  版本：   0.2
  创建于:  2009-03-20
  描述：
    日志模块内部代码使用的函数集合

  修改历史： 
  日期：     修改人：     修改描述：
  
 *********************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "memory.h"  /* 内存调试用头文件 */
#include "log.h"
#include "log_pthread.h"

#define ALIGN(len, s) (((len)+(s)-1)/(s)*(s))

#if LOGDBG   /* 日志模块调试函数，打印日志缓冲区内存数据 */
void dumpLogarea (void)
{
    struct LOG_MSG_T *pstMsg;

    logdbg(stderr, "\n==== area: pStart addr = %p, pEnd addr = %p ====\n",
            g_pstLogArea->pStart, g_pstLogArea->pEnd);
    logdbg(stderr, "|addr     |pNext     |iPrio|pstMsg\n");

    for (pstMsg = (struct LOG_MSG_T *)g_pstLogArea->pHead; (void *)pstMsg != g_pstLogArea->pTail;
            pstMsg = pstMsg->pNext)
        logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)pstMsg, pstMsg->pNext,
                pstMsg->iPrio, (char *)&psgMsg->pcMsg);

    logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)pstMsg, psgMsg->pNext,
            pstMsg->iPrio, (char *)&psgMsg->pcMsg);

    logdbg(stderr, "\n\n");
}
#endif

static int initLogarea (int iBufferSize)
{
    logdbg(stderr,"enter initLogarea\n");
    g_pstLogArea = (struct LOG_AREA_T *)MALLOC(sizeof(struct LOG_AREA_T));

    if (!g_pstLogArea)
        return 1;

    if (iBufferSize < MAX_MSG_SIZE)
        iBufferSize = DEFAULT_AREA_SIZE;

    g_pstLogArea->pStart = MALLOC(iBufferSize);
    memset(g_pstLogArea->pStart, 0, iBufferSize);

    if (!g_pstLogArea->pStart) {
        FREE(g_pstLogArea);
        return 1;
    }

    g_pstLogArea->iEmpty = 1;
    g_pstLogArea->pEnd = g_pstLogArea->pStart + iBufferSize;
    g_pstLogArea->pHead = g_pstLogArea->pStart;
    g_pstLogArea->pTail = g_pstLogArea->pStart;

    g_pstLogArea->pcBuff = MALLOC(MAX_MSG_SIZE + sizeof(struct LOG_MSG_T));

    if (!g_pstLogArea->pcBuff) {
        FREE(g_pstLogArea->pStart);
        FREE(g_pstLogArea);
        return 1;
    }
    return 0;

}


/******************************************************************************
  函数名：initLog
  功能描述：
    初始化日志配置参数和日志缓冲区

  输入参数：
    char *   待记录日志的进程标识
    int      日志缓冲区大小

  输出参数：
    无

  返回值：
    0   初始化成功
    -1  初始化出错
 *****************************************************************************/
int initLog(char *program_name, int iBufferSize)
{
    logdbg(stderr,"enter initLog\n");
    /* 读取日志配置文件 */
    pstConfigCache = (CONFIG_CACHE_T *)malloc(sizeof(CONFIG_CACHE_T));
    if (pstConfigCache == NULL)
    {
        fprintf(stderr, "%s %s %d # 申请日志缓存空间失败\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }

    int iRetVal = initConfigCache(LOG_CONFIG, pstConfigCache);
    if(0 != iRetVal)
    {
        fprintf(stderr, "%s %s %d # %s %d\n", __FILE__, __FUNCTION__, __LINE__, "日志模块初始化出错，错误状态码为：", iRetVal);
        destroyConfigCache(pstConfigCache);
        if (pstConfigCache != NULL)
        {
            free(pstConfigCache);
            pstConfigCache = NULL;
        }
        return -1;
    }

    char **pcLogLevelValue = findInConfigCacheByKey(pstConfigCache, LOG_LEVEL_KEY);
    char **pcLogPlaceValue = findInConfigCacheByKey(pstConfigCache, LOG_PLACE_KEY);
    if (pcLogLevelValue != NULL)
    {
        if (pcLogLevelValue[0] != NULL)
        {
            iLOGLEVEL = atoi(pcLogLevelValue[0]);
        }
    }
    else
    {
        iLOGLEVEL = LOG_ERR;
    }
    freeConfigCacheValues(pcLogLevelValue);

    if (pcLogPlaceValue != NULL)
    {
        if (pcLogPlaceValue[0] != NULL)
        {
            iLogPlace = atoi(pcLogPlaceValue[0]);
        }
    }
    else
    {
        iLogPlace = OUT_CONSOLE;
    }
    freeConfigCacheValues(pcLogPlaceValue);

    char **pcLogFileValue;
    switch(iLogPlace)
    {
        case OUT_CONSOLE:
            pLogFile = stderr;
            break;
        case OUT_FILE:
            pcLogFileValue = findInConfigCacheByKey(pstConfigCache, LOG_FILE_KEY);
            if (pcLogFileValue != NULL)
            {
                if (pcLogFileValue[0] != NULL)
                {
                    if (NULL == (pLogFile = fopen(pcLogFileValue[0], "a")))
                    {
                        dbgprint("输出日志文件打开失败？权限？\n");
                        pLogFile = stderr;
                    }

                }
                else
                {
                    dbgprint("未找到输出日志文件的文件名？配置文件错误？\n");
                    pLogFile = stderr;
                }
            }
            else
            {
                dbgprint("未找到输出文件的文件名？内存分配错误？\n");
                pLogFile = stderr;
            }
            freeConfigCacheValues(pcLogFileValue);
            break;
        case OUT_DB: /* TODO 输出到数据库的设置 */
            pLogFile = stderr;
            break;
        case OUT_SYSLOG: /* 输出到syslog的设置 */
            /* 打开syslog接口 */
            setlogmask(LOG_UPTO(iLOGLEVEL));
            openlog(program_name, LOG_PID | LOG_CONS | LOG_NDELAY, LOG_LOCAL2);
            pLogFile = stderr;
            break;
        case OUT_SOCK:  /* 通过socket输出到日志服务器 */
            pLogFile = stderr;
            break;
        default:
            pLogFile = stderr;
            break;
    }


    if (initLogarea(iBufferSize))
        return -1;

    return 0;
}

void freeLogarea (void)
{
    FREE(g_pstLogArea->pStart);
    FREE(g_pstLogArea->pcBuff);
    FREE(g_pstLogArea);
    switch(iLogPlace)
    {
        case OUT_CONSOLE:
            break;
        case OUT_FILE:
            fclose(pLogFile);
            break;
        case OUT_DB:     /* TODO 输出到数据库的设置 */
            break;
        case OUT_SYSLOG: /* 输出到syslog的设置 */
            closelog();
            break;
        case OUT_SOCK:   /* 通过socket输出到日志服务器 */
            break;
        default:
            break;
    }

    return;
}

void closeLog (void)
{
    freeLogarea();
    closelog();

    return;
}

/******************************************************************************
  函数名：_log_r
  功能描述：
    写日志的主调函数

  输入参数：
    int     消息级别
    char *  输出消息的格式
  ...     输出消息的内容(可变参数)

  输出参数：
    无

  返回值：
    无
 *****************************************************************************/
void _log_r (int iPrio, char * fmt, ...)
{
    va_list ap;
    int iThres;
    extern int iLOGLEVEL;
    iThres = iLOGLEVEL;  /* 消息输出级别阈值 */
    va_start(ap, fmt);

    if (iPrio <= iThres) 
    {
#if _MAIN_H_
        logSafe(iPrio, fmt, ap);
#else
        vfprintf(stderr, fmt, ap);
#endif
    }
    va_end(ap);
}

int enLogQueue (int iPrio, const char *fmt, va_list ap)
{
    int len, fwd;
    char pcBuff[MAX_MSG_SIZE];
    struct LOG_MSG_T * pstMsg;
    struct LOG_MSG_T * lastmsg;

    lastmsg = (struct LOG_MSG_T *)g_pstLogArea->pTail;

    if (!g_pstLogArea->iEmpty) {
        fwd = sizeof(struct LOG_MSG_T) +
            strlen((char *)&lastmsg->pcMsg) * sizeof(char) + 1;
        g_pstLogArea->pTail += ALIGN(fwd, sizeof(void *));
    }
    vsnprintf(pcBuff, MAX_MSG_SIZE, fmt, ap);
    /* 释放时间戳字符串占用的临时分配内存 */
    char *pcTS = va_arg(ap, char *);
    if (pcTS != NULL)
    {
        free(pcTS);
        pcTS = NULL;
    }
    len = ALIGN(sizeof(struct LOG_MSG_T) + strlen(pcBuff) * sizeof(char) + 1,
            sizeof(void *));

    /* not enough space on pTail : rewind */
    if (g_pstLogArea->pHead <= g_pstLogArea->pTail && len > (g_pstLogArea->pEnd - g_pstLogArea->pTail)) {
        logdbg(stderr, "enqueue: rewind pTail to %p\n", g_pstLogArea->pTail);
        g_pstLogArea->pTail = g_pstLogArea->pStart;

        if (g_pstLogArea->iEmpty)
            g_pstLogArea->pHead = g_pstLogArea->pStart;
    }

    /* not enough space on pHead : drop pstMsg */
    if (g_pstLogArea->pHead > g_pstLogArea->pTail && len >= (g_pstLogArea->pHead - g_pstLogArea->pTail)) {
        logdbg(stderr, "enqueue: log area overrun, drop pstMsg\n");

        if (!g_pstLogArea->iEmpty)
            g_pstLogArea->pTail = lastmsg;

        return 1;
    }

    /* ok, we can stage the pstMsg in the area */
    g_pstLogArea->iEmpty = 0;
    pstMsg = (struct LOG_MSG_T *)g_pstLogArea->pTail;
    pstMsg->iPrio = iPrio;
    memcpy((void *)&pstMsg->pcMsg, pcBuff, strlen(pcBuff));
    lastmsg->pNext = g_pstLogArea->pTail;
    pstMsg->pNext = g_pstLogArea->pHead;

    logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)pstMsg, pstMsg->pNext,
            pstMsg->iPrio, (char *)&pstMsg->pcMsg);

#if LOGDBG
    dumpLogarea();
#endif
    return 0;
}

int deLogQueue (void *pcBuff)
{
    struct LOG_MSG_T *src = (struct LOG_MSG_T *)g_pstLogArea->pHead;
    struct LOG_MSG_T *dst = (struct LOG_MSG_T *)pcBuff;
    struct LOG_MSG_T *lst = (struct LOG_MSG_T *)g_pstLogArea->pTail;

    if (g_pstLogArea->iEmpty)
        return 1;

    int len = strlen((char *)&src->pcMsg) * sizeof(char) +
        sizeof(struct LOG_MSG_T) + 1;

    dst->iPrio = src->iPrio;
    memcpy(dst, src,  len);

    if (g_pstLogArea->pTail == g_pstLogArea->pHead)
        g_pstLogArea->iEmpty = 1; /* we purge the last LOG_MSG_T */
    else {
        g_pstLogArea->pHead = src->pNext;
        lst->pNext = g_pstLogArea->pHead;
    }
    logdbg(stderr, "dequeue: %p, %p, %i, %s\n",
            (void *)src, src->pNext, src->iPrio, (char *)&src->pcMsg);
    logAppender(src->iPrio, (char *)&src->pcMsg);

    memset((void *)src, 0,  len);

    return g_pstLogArea->iEmpty;
}

void logSyslog (void *pcBuff)
{
#ifdef _DEBUG_
    struct LOG_MSG_T * pstMsg = (struct LOG_MSG_T *)pcBuff;
    syslog(pstMsg->iPrio, "%s", (char *)&pstMsg->pcMsg);
#endif
}


char *gettimeofday_r()
{
#if 0
    time_t buf;
    time_t ltime = time(&buf);
    int iLen = strlen("YYYY-MM-DD HH:MM:SS") + 2;
        strftime(buf1, iLen - 1, "%Y-%m-%e %T", localtime_r(&ltime, &buf2));
    return buf1;
#endif
    struct timeval tv;
    gettimeofday(&tv, NULL); 
    int iLen = strlen("YYYY-MM-DD HH:MM:SS") + 2;
    char *buf1 = (char *)malloc(iLen * sizeof(char));
    if (buf1 == NULL)
    {
        return "";
    }
    struct tm buf2;
    strftime(buf1, iLen - 1, "%Y-%m-%d %T", localtime_r(&(tv.tv_sec), &buf2));

    return buf1;
}

